
#include <QPointF>
#include <QtMath>
#include <QDebug>
#include <QPaintEvent>

#include "loadingwidget.h"

struct DotLocations {
    float x;
    float y;

    explicit DotLocations(float x, float y) { this->x = x; this->y = y; }
};

class LoadingWidgetPrivate
{
public:
    LoadingWidgetPrivate(LoadingWidget *parent = nullptr);
    ~LoadingWidgetPrivate();

    void PaintDot();
    void Calculate();

    bool autostart;

    QSize size;
    QPoint offset;
    QColor color;
    int dots_count;
    float dot_diameter_min;
    float dot_diameter_max;
    int update_interval;

    int m_index;
    float square_width;
    float center_distance;
    QList<float> raiis;
    QList<DotLocations> locations;

    QTimer *update_timer;

private:
    LoadingWidget * const q_ptr;
    Q_DECLARE_PUBLIC(LoadingWidget)
};

LoadingWidgetPrivate::LoadingWidgetPrivate(LoadingWidget *parent) : q_ptr(parent)
{
    autostart = false;

    color = Qt::gray;
    dots_count = 8;
    dot_diameter_min = 1.5;
    dot_diameter_max = 3;
    update_interval = 100;

    m_index = 0;
    square_width = 0;
    center_distance = 0;

    update_timer = new QTimer();
    update_timer->setInterval(update_interval);
    QObject::connect(update_timer, &QTimer::timeout, parent, &LoadingWidget::On_Refresh);
}

LoadingWidgetPrivate::~LoadingWidgetPrivate()
{
    if (update_timer->isActive()) {
        update_timer->stop();
    }
    update_timer->deleteLater();
}

void LoadingWidgetPrivate::PaintDot()
{
    Q_Q(LoadingWidget);
    QPainter painter(q);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setPen(color);
    painter.setBrush(color);

    for (int i=0; i<dots_count; i++) {
        painter.setPen(color);
        int radii_index = (m_index + dots_count - i) % dots_count;
        if (radii_index >=0 && radii_index < raiis.count()) {
            float radii = raiis.at(radii_index);
            if (i >= 0 && i < locations.count()) {
                // Draw Ellipse
                painter.drawEllipse(QPointF(locations.at(i).x, locations.at(i).y), radii, radii);
                // Draw Rect
                // painter.drawRect(m_locations.at(i).x, m_locations.at(i).y, radii, radii);
                // Draw Fonts
                // QFont font = this->font();
                // font.setPointSize(radii * 1.2);
                // font.setWeight(75);
                // painter.setFont(font);
                // painter.drawText(QPointF(m_locations.at(i).x, m_locations.at(i).y), QStringLiteral("*"));
            }
        }
    }
}

void LoadingWidgetPrivate::Calculate()
{
    int center_x = offset.x();
    int center_y = offset.y();

    square_width = qMin(size.width(), size.height());
    if (size.width() > size.height()) {
        center_x = size.width() / 4;
    } else if (size.width() < size.height()) {
        center_y = size.height() / 4;
    }

    float half_width = square_width / 2;
    center_distance = half_width - dot_diameter_max / 2 - 1;

    float gap = (dot_diameter_max - dot_diameter_min) / (dots_count - 1) / 2;
    float angle_gap = 360.00 / dots_count;

    locations.clear();
    raiis.clear();

    for (int i=0; i<dots_count; i++) {
        raiis << dot_diameter_max / 2 - i * gap;
        float radian = qDegreesToRadians(-angle_gap * i);
        float dot_x = half_width + center_distance * qCos(radian) + center_x;
        float dot_y = half_width - center_distance * qSin(radian) + center_y;
        locations << DotLocations(dot_x, dot_y);
    }

    m_index++;
}

LoadingWidget::LoadingWidget(bool autostart, const QSize &size, const QRect &geometry, QWidget *parent) : QWidget{parent}, d_ptr(new LoadingWidgetPrivate(this))
{
    this->setWindowFlags(Qt::FramelessWindowHint);
    this->setGeometry(geometry);

    Q_D(LoadingWidget);
    d->autostart = autostart;
    if (size.width() == 0 || size.height() == 0) {
        d->size = this->geometry().size();
    } else {
        d->size = size;
    }
    if (d->size == this->geometry().size()) {
        d->offset = QPoint(0, 0);
    } else {
        d->offset = QPoint((this->geometry().size().width() - d->size.width()) / 2, (this->geometry().size().height() - d->size.height()) / 2);
    }
    qDebug().noquote() << "LoadingWidget:" << size << geometry;
    if (d->autostart) {
        start();
    }
}

LoadingWidget::~LoadingWidget()
{}

void LoadingWidget::SetDotCount(int dots)
{ Q_D(LoadingWidget); d->dots_count = dots; }

void LoadingWidget::SetDotColor(const QColor &color)
{ Q_D(LoadingWidget); d->color = color; }

void LoadingWidget::SetDotDiameter(float min, float max)
{ Q_D(LoadingWidget); d->dot_diameter_min = min; d->dot_diameter_max = max; }

void LoadingWidget::SetInterval(int interval)
{ Q_D(LoadingWidget); d->update_interval = interval; }

void LoadingWidget::start()
{ Q_D(LoadingWidget); d->update_timer->start(); this->setVisible(true); }

void LoadingWidget::stop()
{ Q_D(LoadingWidget); d->update_timer->stop(); this->setVisible(false); }

void LoadingWidget::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event)

    Q_D(LoadingWidget);
    d->PaintDot();
}

void LoadingWidget::resizeEvent(QResizeEvent *event)
{
    Q_UNUSED(event)

    Q_D(LoadingWidget);
    d->Calculate();
}

void LoadingWidget::On_Refresh()
{
    Q_D(LoadingWidget);
    d->Calculate();

    repaint();
    // update();
}

